class Vue {
    constructor(options) {
        this.$options = options
        // TODO: data可能是函数
        this._data = options.data
        this.initData()
    }

    initData() {
        let data = this._data
        let keys = Object.keys(data)
        for(let i=0;i<keys.length;i++){
            Object.defineProperty(this, keys[i], {
                enumerable: true,
                configurable: true,
                get: function proxyGetter() {
                    return data[keys[i]]
                },
                set: function proxySetter(value) {
                    data[keys[i]] = value
                }
            })
        }
        observe(data)
        this.initWatch()
    }

    initWatch() {
        let watch = this.$options.watch
        if(watch){
            let keys = Object.keys(watch)
            for(let i=0;i<keys.length;i++){
                new Watcher(this, keys[i], watch[keys[i]])
            }
        }
    }
    
    $watch(key,cb){
        new Watcher(this, key, cb)
    }
}

function observe(data) {
    let type = Object.prototype.toString.call(data)
    if(type !== '[object Object]' && type !== '[object Array]') return
    new Observer(data)
}

function defineReactive(obj, key, value) {
    observe(obj[key])
    let dep = new Dep()
    Object.defineProperty(obj, key, {
        enumerable: true,
        configurable: true,
        get: function reactiveGetter() {
            dep.depend()
            return value
        },
        set: function reactiveSetter(val) {
            if(val === value) return        
            value = val
            dep.notify()
        }
    })
}

class Observer {
    constructor(data) {
        this.walk(data)
    }
    walk(data) {
        let keys = Object.keys(data)
        for(let i=0;i<keys.length;i++){
            // console.log(keys[i], data[keys[i]]);
            defineReactive(data, keys[i], data[keys[i]])
        }
    }
}

// 收集依赖的部分
class Dep {
    constructor() {
        this.subs = []
    }

    depend() {
        if(Dep.target){
            this.subs.push(Dep.target)
        }
    }

    notify() {
        this.subs.forEach((watcher) => {
            // 依次执行回调函数
            watcher.run()
        })
    }
}

class Watcher {
    constructor(vm, exp, cb) {
        this.vm = vm
        this.exp = exp    
        this.cb = cb
        this.get()
    }

    // 求值
    get() {
        Dep.target = this
        this.vm[this.exp]
        Dep.target = undefined
    }
    
    run() {
        this.cb.call(this.vm)
    }
}